#include "netstat.h"

#include <QHostInfo>
#include <netdb.h>

NetStat::NetStat(QObject *parent) : QObject(parent)
{
    file_tcp = QString("/proc/net/tcp");
    file_udp = QString("/proc/net/udp");

    tcp_state.append("");
    tcp_state.append("ESTABLISHED");
    tcp_state.append("SYN_SENT");
    tcp_state.append("SYN_RECV");
    tcp_state.append("FIN_WAIT1");
    tcp_state.append("FIN_WAIT2");
    tcp_state.append("TIME_WAIT");
    tcp_state.append("CLOSE");
    tcp_state.append("CLOSE_WAIT");
    tcp_state.append("LAST_ACK");
    tcp_state.append("LISTEN");
    tcp_state.append("CLOSING");

    keyWork = true;

    qre.setPattern( "(socket:\\[(\\d)*\\])" );
}

void NetStat::tryStopNetStat()
{
    keyWork = false;
}

void NetStat::workNetStat()
{
    keyWork = true;
}

#if 0
QString NetStat::convert_ipaddr( const QString &str_ip_addr ) const
{
    bool ok;
    quint32 ip_int = str_ip_addr.toInt(&ok, 16);
    QHostAddress ha( ip_int );
    return ha.toString();
}
#endif
QString NetStat::convert_ipaddr(const QString &str_ip_addr) const
{
    QByteArray ba = str_ip_addr.toAscii();
    bool ok;
    int b1 = ( QString("0x") + QString(str_ip_addr[6]) + QString(str_ip_addr[7]) ).toInt(&ok, 16);
    int b2 = ( QString("0x") + QString(str_ip_addr[4]) + QString(str_ip_addr[5]) ).toInt(&ok, 16);
    int b3 = ( QString("0x") + QString(str_ip_addr[2]) + QString(str_ip_addr[3]) ).toInt(&ok, 16);
    int b4 = ( QString("0x") + QString(str_ip_addr[0]) + QString(str_ip_addr[1]) ).toInt(&ok, 16);
    QString ret = QString::number(b1, 10) + QString(".") + QString::number(b2, 10) + QString(".");
    ret += QString::number(b3, 10) + QString(".") + QString::number(b4, 10);
    return ret;
}


/*   =====(  TCP  )===== */
QList<QMap<QString, QVariant>*> NetStat::getStatTCP( const bool resolve )
{
    QList<QMap<QString, QVariant>*> retList;

    QFile file( file_tcp );
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return retList;
    }

    QTextStream in(&file);
    in.readLine();

    while ( !in.atEnd() ) {
        QString line = in.readLine();
        //qDebug() << "NetStat::getStatTCP() " <<line;

        if( keyWork == false ) {
            break;
        }

        QMap<QString, QVariant> *bufConnection = new QMap<QString, QVariant>();
        bool ok;

        QStringList elements_line = line.split( QRegExp("\\s+"), QString::SkipEmptyParts );
        QStringList local_addr_l = (elements_line.at(1)).split(":");
        QStringList foreign_addr_l = (elements_line.at(2)).split(":");
        if( resolve ) {
            /* локальный адрес - имя */
            QHostInfo hi_la = QHostInfo::fromName( this->convert_ipaddr(local_addr_l.at(0)) );
            bufConnection->insert( "local_addr", hi_la.hostName() );

            /* локальный порт сервис */
            int port = (QString("0x") + local_addr_l.at(1)).toInt(  &ok, 16 );
            if( ok ) {
                unsigned short _port = htons( port );
                servent *res = getservbyport( _port, "tcp" );
                if( res != NULL ){
                    //qDebug() << "local port(s): " << res->s_name;
                    bufConnection->insert( "local_port", QString(res->s_name) );
                } else {
                    bufConnection->insert( "local_port", QString::number(port) );
                }
            } else {
                bufConnection->insert( "local_port", (QString("0x") + local_addr_l.at(1)).toInt(  &ok, 16 ) );
            }

            /* удаленный адрес - имя */
            QHostInfo hi_fa = QHostInfo::fromName( this->convert_ipaddr(foreign_addr_l.at(0)) );
            bufConnection->insert( "foreign_addr", hi_fa.hostName() );

            /* удаленный порт - сервис */
            port = (QString("0x") + foreign_addr_l.at(1)).toInt(  &ok, 16 );
            if( ok ) {
                unsigned short _port = htons( port );
                servent *res = getservbyport( _port, "tcp" );
                if( res != NULL ) {
                    //qDebug() << "foreign port(s):" << res->s_name;
                    bufConnection->insert( "foreign_port", QString(res->s_name) );
                } else {
                    bufConnection->insert( "foreign_port", QString::number(port) );
                }
            } else {
                bufConnection->insert( "foreign_port", (QString("0x") + foreign_addr_l.at(1)).toInt(  &ok, 16 ) );
            }

        } else {
            bufConnection->insert( "local_addr", this->convert_ipaddr(local_addr_l.at(0)) );
            bufConnection->insert( "local_port", (QString("0x") + local_addr_l.at(1)).toInt(  &ok, 16 ) );
            bufConnection->insert( "foreign_addr", this->convert_ipaddr(foreign_addr_l.at(0)) );
            bufConnection->insert( "foreign_port", (QString("0x") + foreign_addr_l.at(1)).toInt(  &ok, 16 ) );
        }
        int stat = (QString("0x") + elements_line.at(3)).toInt(  &ok, 16 );
        bufConnection->insert("state", tcp_state.at(stat) );

        QString inode_s = (elements_line.at(9));
        int inode = (elements_line.at(9)).toInt( &ok, 10 );
        bufConnection->insert("inode", inode_s );

        /* получение PID по Inode сокета */
        int pid = this->getPidByInode( inode );
        if( pid != 0 ) {
            QString cmdline = this->getInfoProcByPid( pid );
            bufConnection->insert("pid", pid);
            bufConnection->insert("cmd", cmdline);
        } else {
            bufConnection->insert( "pid", "" );
            bufConnection->insert( "cmd", "" );
        }
        //qDebug() << "NetStat::getStatTCP() " <<bufConnection;

        retList.append( bufConnection );
    }

    file.close();
    return retList;
}


/*   =====(  UDP  )===== */
QList<QMap<QString, QVariant>*> NetStat::getStatUDP( const bool resolve )
{
    QList<QMap<QString, QVariant>*> retList;

    QFile file( file_udp );
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return retList;
    }

    QTextStream in(&file);
    in.readLine();

    while ( !in.atEnd() ) {
        QString line = in.readLine();
        //qDebug() << "NetStat::getStatUDP() " <<line;

        if( keyWork == false ) {
            break;
        }

        QMap<QString, QVariant> *bufConnection = new QMap<QString, QVariant>();
        bool ok;

        QStringList elements_line = line.split( QRegExp("\\s+"), QString::SkipEmptyParts );
        QStringList local_addr_l = (elements_line.at(1)).split(":");
        QStringList foreign_addr_l = (elements_line.at(2)).split(":");
        if( resolve ) {
            /* локальный адрес - имя */
            QHostInfo hi_la = QHostInfo::fromName( this->convert_ipaddr(local_addr_l.at(0)) );
            bufConnection->insert( "local_addr", hi_la.hostName() );

            /* локальный порт сервис */
            int port = (QString("0x") + local_addr_l.at(1)).toInt(  &ok, 16 );
            if( ok ) {
                unsigned short _port = htons( port );
                servent *res = getservbyport( _port, "udp" );
                if( res != NULL ){
                    //qDebug() << "local port(s): " << res->s_name;
                    bufConnection->insert( "local_port", QString(res->s_name) );
                } else {
                    bufConnection->insert( "local_port", QString::number(port) );
                }
            } else {
                bufConnection->insert( "local_port", (QString("0x") + local_addr_l.at(1)).toInt(  &ok, 16 ) );
            }

            /* удаленный адрес - имя */
            QHostInfo hi_fa = QHostInfo::fromName( this->convert_ipaddr(foreign_addr_l.at(0)) );
            bufConnection->insert( "foreign_addr", hi_fa.hostName() );

            /* удаленный порт - сервис */
            port = (QString("0x") + foreign_addr_l.at(1)).toInt(  &ok, 16 );
            if( ok ) {
                unsigned short _port = htons( port );
                servent *res = getservbyport( _port, "udp" );
                if( res != NULL ) {
                    //qDebug() << "foreign port(s):" << res->s_name;
                    bufConnection->insert( "foreign_port", QString(res->s_name) );
                } else {
                    bufConnection->insert( "foreign_port", QString::number(port) );
                }
            } else {
                bufConnection->insert( "foreign_port", (QString("0x") + foreign_addr_l.at(1)).toInt(  &ok, 16 ) );
            }

        } else {
            bufConnection->insert( "local_addr", this->convert_ipaddr(local_addr_l.at(0)) );
            bufConnection->insert( "local_port", (QString("0x") + local_addr_l.at(1)).toInt(  &ok, 16 ) );
            bufConnection->insert( "foreign_addr", this->convert_ipaddr(foreign_addr_l.at(0)) );
            bufConnection->insert( "foreign_port", (QString("0x") + foreign_addr_l.at(1)).toInt(  &ok, 16 ) );
        }
        int stat = (QString("0x") + elements_line.at(3)).toInt(  &ok, 16 );
        if( stat == 1 ) {
            bufConnection->insert("state", "ESTABLISHED" );
        } else if( stat == 7 ) {
            bufConnection->insert("state", "" );
        }
        else {
            bufConnection->insert("state", "UNKNOWN" );
        }

        QString inode_s = (elements_line.at(9));
        int inode = (elements_line.at(9)).toInt( &ok, 10 );
        bufConnection->insert("inode", inode_s );

        /* получение PID по Inode сокета */
        int pid = this->getPidByInode( inode );
        if( pid != 0 ) {
            QString cmdline = this->getInfoProcByPid( pid );
            bufConnection->insert("pid", pid);
            bufConnection->insert("cmd", cmdline);
        } else {
            bufConnection->insert( "pid", "" );
            bufConnection->insert( "cmd", "" );
        }
        //qDebug() << "NetStat::getStatUDP() " <<bufConnection;

        retList.append( bufConnection );
    }

    file.close();
    return retList;
}


int NetStat::getPidByInode( const int __inode )
{
    int ret = 0;
    QDir p("/proc");
    QStringList listdirVar = p.entryList( QDir::AllDirs );
    QStringList::const_iterator itr;
    for( itr = listdirVar.constBegin(); itr != listdirVar.constEnd(); itr++ )
    {
        if( keyWork == false ) { break; }
        QString pid_proc = *itr; /* текущее имя каталога, у которого имя совпадает с PID некого процесса в системе */
        QDir p_list_files_fd( (QString("/proc/") + pid_proc + QString("/fd/")) );
        QStringList list_files_fd = p_list_files_fd.entryList( QDir::Files );
        QStringList::const_iterator itr_fd;
        for( itr_fd = list_files_fd.constBegin(); itr_fd != list_files_fd.constEnd(); itr_fd++ )
        {
            if( keyWork == false ) { break; }
            QString F = *itr_fd;
            QString ff("/proc/" + pid_proc + "/fd/" + F );
            QString name_file = QFile::symLinkTarget( ff );
            if( name_file.isEmpty() ) {
                continue;
            } else {
                int m = qre.indexIn(name_file);
                if( m != -1 ) {
                    name_file = name_file.mid( m );
                    int LL = name_file.length();
                    name_file = name_file.remove( (LL-1), 1);
                    QString inode = name_file.mid( 8, (LL-1) );
                    bool ok;
                    int i_inode = inode.toInt(&ok, 10);
                    if( (i_inode == __inode) && ok ) {
                        return ( pid_proc.toInt(&ok,10) );
                    }
                }
            }
        }
    }

    return ret;
}

QString NetStat::getInfoProcByPid( const int pid )
{
    QString ret = "";

    QFile file( (QString("/proc/")+QString::number(pid)+QString("/cmdline")) );
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return ret;
    }

    QTextStream in(&file);
    ret = in.readLine();
    file.close();

    ret = ret.replace( QChar(0x00), QChar(0x20) );
    ret.trimmed();
    //qDebug() << "[" << ret << "]";

    return ret;
}
